/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include <dylab/utils/types/dynamicCast.h>
#include <dylab/utils/math/Matrix3x3.h>
#include <dylab/simobject/SimObject.h>

//===========================================================================================
#include "TetraComplex_ModelStructure.h"

//===========================================================================================
const dylab::String dylab::TetraComplex_ModelStructure::COMPONENT_NAME = "t4complex";

//===========================================================================================
// Constructors & destructor
//===========================================================================================
dylab::TetraComplex_ModelStructure::TetraComplex_ModelStructure() 
: ComModelStructure(COMPONENT_NAME), MassPointsFeatureBase(this)
, TetrahedralVolumeFeatureBase(this), BoundaryMeshFeatureBase(this)
, mCurrNodeIDCap(0)
, mBoundaryMeshGeometryNeedsUpdate(false), mBoundaryMeshTopologyNeedsUpdate(false)
, mNodeDataContainer(true), mEdgeDataContainer(true), mTriDataContainer(true), mTetraDataContainer(true)
, mBoundaryVertexContainer(true), mBoundaryFaceContainer(true)
, mParentSimObject(NULL)
{   
}
//-----------------------------------------------------------------------------
dylab::TetraComplex_ModelStructure::~TetraComplex_ModelStructure() 
{
    for (Simplex::OrderIterator order; order.isValid(); order++)
        getSimplexListeners(order).remove(this);
}
//-----------------------------------------------------------------------------

//===========================================================================================
// ISimplexListener methods
//===========================================================================================
void dylab::TetraComplex_ModelStructure::onSimplexAdded(Simplex * simplex)
{
    switch (simplex->getOrder())
    {
    case NodeSimplex::ORDER:
        // ignore, is processed directly
        break;
    case EdgeSimplex::ORDER:
        mEdgeDataContainer.insert(simplex->getId(), new _Edge(dynamicCast<EdgeSimplex>(simplex), this));
        break;
    case TriSimplex::ORDER:
        mTriDataContainer.insert(simplex->getId(), new _Triangle(dynamicCast<TriSimplex>(simplex), this));        
        break;
    case TetraSimplex::ORDER:
        // ignore, is processed directly        
        break;
    }

    // debug log
    DYLAB_TRACEF2("New simplex added: order = %2%, id = %1%", simplex->getId(), simplex->getOrder());
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::onSimplexRemoved(Simplex * simplex)
{
    DYLAB_ASSERT(simplex != NULL);

    Simplex::id_t simplexID = simplex->getId();
    //uint32_t vertexIndex;

    switch (simplex->getOrder())
    {
    case NodeSimplex::ORDER:
        //if (mNodeVertexMap.reader().find(simplexID, &vertexIndex))
        //{
        //    BoundaryVertexContainer::Writer(mBoundaryVertexContainer).remove(vertexIndex vector
        //    mNodeVertexMap.writer().remove(simplex);
        //}
        mNodeDataContainer.remove(simplexID);
        break;
    case EdgeSimplex::ORDER:
        mEdgeDataContainer.remove(simplexID);
        break;
    case TriSimplex::ORDER:
        // TODO: bool removeTry(...)
        //if (mBoundaryFaceContainer.reader().has(simplexID))
        //    BoundaryFaceContainer::Writer(mBoundaryFaceContainer).remove(simplexID);
        mTriDataContainer.remove(simplexID);
        break;
    case TetraSimplex::ORDER:
        mTetraDataContainer.remove(simplexID);
        break;
    }

    // debug log
    DYLAB_TRACEF2("Simplex removed: order = %2%, id = %1%", simplexID, simplex->getOrder());
}
//-----------------------------------------------------------------------------
dylab::SimplicalComplex::ISimplexListener * dylab::TetraComplex_ModelStructure::clone() const
{
    // no clone allowen
    return NULL;
}
//-----------------------------------------------------------------------------

//===========================================================================================
// ISimObjectComponent methods
//===========================================================================================
dylab::String dylab::TetraComplex_ModelStructure::getDescription() const
{
	return String::fmt("%1%", getFullName())
		 + String::fmt("\tiMP: mp=%1%", mNodeDataContainer.getCount())
		 + String::fmt("\tiT4V: e=%1%,t3=%2%,t4=%3%", mEdgeDataContainer.getCount(), mTriDataContainer.getCount(), 
			 mTetraDataContainer.getCount())
		 + String::fmt("\tiBM: v=%1%,f=%2%", mBoundaryVertexContainer.getCount(), mBoundaryFaceContainer.getCount());		
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::initialize(SimObject * _parentSimObject)
{
	// check & store pointer to the parent simulation object
	DYLAB_ASSERT(_parentSimObject != NULL);
	mParentSimObject = _parentSimObject;

	// instert itself as a listener to all created & destroyed simplices
    for (Simplex::OrderIterator order; order.isValid(); order++)
        getSimplexListeners(order).insertAtEnd(this);    
}
//-----------------------------------------------------------------------------

//===========================================================================================
// MassPointsFeatureBase methods
//===========================================================================================
dylab::MassPoint::id_t 
dylab::TetraComplex_ModelStructure::createMassPoints(uint32_t count)
{
    // create count new mps (the mass points are created as a side effect)
    for (uint32_t i = 0; i < count; i++)        
        mNodeDataContainer.insert(mCurrNodeIDCap + i, new _MassPoint(new NodeSimplex(mCurrNodeIDCap + i, this))); 

    // shift the cap
    mCurrNodeIDCap += count;

    // return id of the first mass point in the created sequence
    return mCurrNodeIDCap - count;
}
//-----------------------------------------------------------------------------
dylab::MassPointContainer::Iterator 
dylab::TetraComplex_ModelStructure::massPointIterator()
{
    // TODO: better need-for-update check
    //mBoundaryMeshTopologyNeedsUpdate = true;
    mBoundaryMeshGeometryNeedsUpdate = true;

    // return the node data container iterator
    return NodeDataContainer::Iterator(mNodeDataContainer);
}
//-----------------------------------------------------------------------------
dylab::MassPointContainer::ConstIterator 
dylab::TetraComplex_ModelStructure::massPointIterator() const
{
    // return the node data container iterator
    return NodeDataContainer::ConstIterator(mNodeDataContainer);
}
//-----------------------------------------------------------------------------
dylab::MassPointContainer::Reader 
dylab::TetraComplex_ModelStructure::massPointReader() const
{
    // return the reader of the node data container
    return NodeDataContainer::Reader(mNodeDataContainer);
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::prepareForPointRendering()
{
    // nothing special is needed to be done here
}
//-----------------------------------------------------------------------------
dylab::MassPoint::id_t
dylab::TetraComplex_ModelStructure::getNearestMassPointId(const Vector3 & _pos)
{
    bool found = false;
    MassPoint::id_t nearestMassPointId = 0;
    real_t nearestDist = NumericTraits<real_t>::maximum();

    for (NodeDataContainer::Iterator node(mNodeDataContainer); node.isValid(); node++)
    {
        real_t dist = (_pos - node->currState().position).length();
        if (dist < nearestDist)
        {
            found = true;
            nearestDist = dist;
            nearestMassPointId = node->getId();
        }
    }

    if (!found)
        throw EDoesNotExists("There are no mass points!");

    return nearestMassPointId;
}
//-----------------------------------------------------------------------------

//===========================================================================================
// TetrahedralVolumeFeatureBase methods
//===========================================================================================
dylab::Tetrahedron::id_t 
dylab::TetraComplex_ModelStructure::createTetrahedron(MassPoint::id_t _mp1, MassPoint::id_t _mp2, 
	MassPoint::id_t _mp3, MassPoint::id_t _mp4)
{
    // first create complex tetrahedron
    TetraSimplex * tetra = new TetraSimplex(this, _mp1, _mp2, _mp3, _mp4);
    DYLAB_ASSERT(tetra != NULL);

    // then insert its data companion into the container
    mTetraDataContainer.insert(tetra->getId(), 
        new _Tetrahedron(tetra, massPointReader().get(_mp1), massPointReader().get(_mp2),
                                massPointReader().get(_mp3), massPointReader().get(_mp4)));

    TetrahedronGeometry tetraGeom = mTetraDataContainer.get(tetra->getId())->getGeometry();
    if (tetraGeom.isDegenerated())
	{
        DYLAB_TRACEF2("Degenerated tetrahedron has born: %1% (value=%2%)", tetraGeom, tetraGeom.computeValue());
	}

	// return the id of the newly created tetrahedron
	return tetra->getId();
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::destroyTetrahedron(Tetrahedron::id_t _tetraID)
{
    // remove the tetrahedron specified by its id
    destroyTetra(_tetraID);
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::subdivideTetrahedron(Tetrahedron::id_t _tetraID, const Vector3 & _point)
{
    // log info
    DYLAB_TRACEF2("Tetrahedron %1% is to be subdivided by %2%", _tetraID, _point);

    // first, create new node for the _point
    MassPoint::id_t newNodeID = createMassPoints(1);
    MassPoint * mp = mNodeDataContainer.get(newNodeID);
    mp->currState().position = _point;
    // TODO: generate other necessary mass point attributes

    // then, create the 4 corresponding tetrahedra (one face and new node each)
    TetraSimplex * tetraSimplex = getTetra(_tetraID);
    for (TetrahedronGeometry::FaceIdxIterator faceIdx; faceIdx.isValid(); faceIdx++)
    {
        nodeSimplexID_t faceNodeIDs[TriangleGeometry3::V_COUNT];
        tetraSimplex->getFaceNodeIDs(faceIdx, faceNodeIDs);
        createTetrahedron(faceNodeIDs[TriangleGeometry3::V_0], faceNodeIDs[TriangleGeometry3::V_2], faceNodeIDs[TriangleGeometry3::V_1], newNodeID);
    }

    // and finally remove the old tetrahedron
    destroyTetrahedron(_tetraID);
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::subdivideEdge(Edge::id_t _edgeID, const Vector3 & _point)
{
    // log info
    DYLAB_TRACEF3("Edge %1% (%2%) is to be subdivided by %3%", mEdgeDataContainer.get(_edgeID)->getGeometry(), _edgeID, _point);

    // create new node for the point
    MassPoint::id_t newNodeID = createMassPoints(1);
    MassPoint * mp = mNodeDataContainer.get(newNodeID);
    mp->currState().position = _point;
    //TODO: generate other necessary mass point attributes

    // find all super-adjacent tetrahedra
    EdgeSimplex * complexEdge = getEdge(_edgeID);
    AdjacentTetras adjacentTetras(complexEdge);

    // walk through all these tetrahedra
    for (AdjacentSimplices::Simplices::Iterator simplex(adjacentTetras.simplicesIterator()); simplex.isValid(); )
    {
        TetraSimplex * tetraSimplex = dynamicCast<TetraSimplex>(simplex());
        DYLAB_TRACEF("\tIts tetrahedron: %1%", tetraSimplex->getId());            

        // create 2 new tetrahedra from the new point and opposite face
        nodeSimplexID_t faceNodeIDs[TriangleGeometry3::V_COUNT];
        for (EdgeGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
        {
            tetraSimplex->convertTriIDToFaceNodeIDs(tetraSimplex->getOppositeTriID(complexEdge->getNodeID(vertexIdx)), faceNodeIDs);
            createTetrahedron(faceNodeIDs[TriangleGeometry3::V_0], faceNodeIDs[TriangleGeometry3::V_2], faceNodeIDs[TriangleGeometry3::V_1], newNodeID);
        }

        // advance in container
        simplex++;

        // remove the original tetrahedron
        destroyTetrahedron(tetraSimplex->getId());
    }
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::subdivideTriangle(Triangle::id_t _triID, const Vector3 & _point)
{
    // log info
    DYLAB_TRACEF2("Triangle %1% is to be subdivided by %2%", _triID, _point);

    // create new node for the point
    MassPoint::id_t newNodeID = createMassPoints(1);
    MassPoint * mp = mNodeDataContainer.get(newNodeID);
    mp->currState().position = _point;
    // TODO: generate other necessary mass point attributes

    // find all super-adjacent tetrahedra
    TriSimplex * triSimplex = getTri(_triID);
    AdjacentTetras adjacentTetras(triSimplex);

    // walk through all these tetrahedra
    for (AdjacentSimplices::Simplices::Iterator simplex(adjacentTetras.simplicesIterator()); simplex.isValid(); )
    {
        TetraSimplex * tetraSimplex = dynamicCast<TetraSimplex>(simplex());
        DYLAB_TRACEF("\tIts tetrahedron: %1%", tetraSimplex->getId());            

        // create 3 new tetrahedra from the new point and opposite faces
        nodeSimplexID_t faceNodeIDs[TriangleGeometry3::V_COUNT];
        for (TriangleGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
        {
            tetraSimplex->convertTriIDToFaceNodeIDs(tetraSimplex->getOppositeTriID(triSimplex->getNodeID(vertexIdx)), faceNodeIDs);
            createTetrahedron(faceNodeIDs[TriangleGeometry3::V_0], faceNodeIDs[TriangleGeometry3::V_2], faceNodeIDs[TriangleGeometry3::V_1], newNodeID);
        }

        // advance in container
        simplex++;

        // remove the original tetrahedron
        destroyTetrahedron(tetraSimplex->getId());
    }
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::collapseEdge(Edge::id_t _edgeID)
{
    // TODO
	DYLAB_NOT_IMPLEMENTED();
}
//-----------------------------------------------------------------------------
dylab::EdgeContainer::Iterator 
dylab::TetraComplex_ModelStructure::edgeIterator()
{
    // return edge container iterator
    return EdgeDataContainer::Iterator(mEdgeDataContainer);
}
//-----------------------------------------------------------------------------
dylab::EdgeContainer::ConstIterator 
dylab::TetraComplex_ModelStructure::edgeIterator() const
{
    // return edge container iterator
    return EdgeDataContainer::ConstIterator(mEdgeDataContainer);
}
//-----------------------------------------------------------------------------
dylab::EdgeContainer::Reader 
dylab::TetraComplex_ModelStructure::edgeReader() const
{
    // return edge container reader
    return EdgeDataContainer::Reader(mEdgeDataContainer);
}
//-----------------------------------------------------------------------------
dylab::TriangleContainer::Iterator 
dylab::TetraComplex_ModelStructure::triangleIterator()
{
    // return triangle container iterator
    return TriDataContainer::Iterator(mTriDataContainer);
}
//-----------------------------------------------------------------------------
dylab::TriangleContainer::ConstIterator 
dylab::TetraComplex_ModelStructure::triangleIterator() const
{
    // return triangle container iterator
    return TriDataContainer::ConstIterator(mTriDataContainer);
}
//-----------------------------------------------------------------------------
dylab::TriangleContainer::Reader 
dylab::TetraComplex_ModelStructure::triangleReader() const
{
    // return triangle container reader
    return TriDataContainer::Reader(mTriDataContainer);
}
//-----------------------------------------------------------------------------
dylab::TetrahedronContainer::Iterator 
dylab::TetraComplex_ModelStructure::tetrahedronIterator()
{
    // return tetrahedron container iterator
    return TetraDataContainer::Iterator(mTetraDataContainer);
}
//-----------------------------------------------------------------------------
dylab::TetrahedronContainer::ConstIterator 
dylab::TetraComplex_ModelStructure::tetrahedronIterator() const
{
    // return tetrahedron container iterator
    return TetraDataContainer::ConstIterator(mTetraDataContainer);
}
//-----------------------------------------------------------------------------
dylab::TetrahedronContainer::Reader 
dylab::TetraComplex_ModelStructure::tetrahedronReader() const
{
    // return tetrahedron container reader
    return TetraDataContainer::Reader(mTetraDataContainer);
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::prepareForVolumeRendering()
{
    // nothing special is needed to be done here
}
//-----------------------------------------------------------------------------

//===========================================================================================
// BoundaryMeshFeatureBase methods
//===========================================================================================
void dylab::TetraComplex_ModelStructure::generateFromMassPoints()
{
	//TODO: not implemented yet
	DYLAB_NOT_IMPLEMENTED();
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::generateFromTetrahedralVolume()
{   
    // initialize the boundary vertex & face container, first phase - build the structures
    for (TriDataContainer::Iterator triData(mTriDataContainer); triData.isValid(); triData++)
    {
        // get the triangle simplex
        Simplex * triSimplex = simplicesReader(TriSimplex::ORDER).get(~triData);

        // if the triangle is boundary (i.e., it is attached to only one super-simplex)
        if (triSimplex->isBoundary())
        {   
            // set the triangle boundary flag
            triData->setBoundary(true);

            // with the help of the complex topology retrieve triangle parent tetrahedron data pointer 
            TetraSimplex * tetraSimplex = 
				dynamicCast<TetraSimplex>(Simplex::OtherSimplices::Iterator(triSimplex->getSuperSimplices())());

            // retrieve face' vertices node ids
            MassPoint::id_t mpIDs[TriangleGeometry3::V_COUNT];            
            tetraSimplex->convertTriIDToFaceNodeIDs(~triData, mpIDs);

            // retrieve face' mps (mass points) pointers
            MassPoint * mps[TriangleGeometry3::V_COUNT];
            for (uint32_t i = 0; i < TriangleGeometry3::V_COUNT; i++)
                mps[i] = mNodeDataContainer.get(mpIDs[i]);

            // compute the face normal
            Vector3 faceNormal = (mps[1]->currState().position - mps[0]->currState().position)
				.cross(mps[2]->currState().position - mps[0]->currState().position).normalise();           

            // declare array for storing face vertices
            Vertex * faceVertices[TriangleGeometry3::V_COUNT];

            // for all face vertices
            for (TriangleGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
            {
                Vertex * vertex;
                uint32_t vertexIndex;
                
                // if mps is in node-vertex map
                if (mNodeVertexMap.find(mpIDs[vertexIdx], &vertexIndex))
                {
                    // retrieve the vertex by the its associated index to the vertex container
                    vertex = mBoundaryVertexContainer[vertexIndex];   
                }
                else
                {
                    // create vertex for that mps & set its position and texture coordinates
                    vertex = new _Vertex(mps[vertexIdx], mBoundaryVertexContainer.getCount(), 
						mps[vertexIdx]->currState().position, Vector3::ZERO, 0, 0);
                    DYLAB_ASSERT(vertex != NULL);
                    
                    // store the created vertex into the vertex container
                    mBoundaryVertexContainer.insertAtEnd(vertex);

                    // store the (mps id), (vertex index) pair into the mps-vertex map
                    mNodeVertexMap.insert(mpIDs[vertexIdx], vertex->getIndex());

					// start the process of calculating the vertex normal 
					vertex->beginNormalCalculation();
                }
                
                // add the face normal to the vertex normal and increment the face-normal counter
                vertex->addFaceNormal(faceNormal);

                // store the appropriate vertex index into the face
                faceVertices[vertexIdx] = vertex;
            } 

            // store the face into the face container
            mBoundaryFaceContainer.insert(~triData, new Face(faceVertices, faceNormal));
        }
    }
    
    // initialize the boundary vertex & face container, second phase - compute vertex normals
    for (BoundaryVertexContainer::Iterator vertex(mBoundaryVertexContainer); vertex.isValid(); vertex++)            
        vertex->endNormalCalculation();    // compute vertex normal

    // no need to update them now
    mBoundaryMeshTopologyNeedsUpdate = false;
    mBoundaryMeshGeometryNeedsUpdate = false;
}        
//-----------------------------------------------------------------------------
dylab::uint32_t 
dylab::TetraComplex_ModelStructure::createVertex(const Vector3 & _position, const Vector3 & _normal, real_t _u, real_t _v)
{
	// store the new vertex index
	uint32_t index = mBoundaryVertexContainer.getCount();

    // store the created vertex into the vertex container
    mBoundaryVertexContainer.insertAtEnd(new _Vertex(NULL, index, _position, _normal, _u, _v));

	// return the index of the newly created vertex
	return index;
}
//-----------------------------------------------------------------------------
dylab::Face::id_t
dylab::TetraComplex_ModelStructure::createFace(uint32_t  _v0, uint32_t  _v1, uint32_t _v2)
{
    // retrieve vertex pointers based on their given indices
    Vertex * faceVertices[TriangleGeometry3::V_COUNT];
    BoundaryVertexContainer::Reader vertices(mBoundaryVertexContainer);
    faceVertices[TriangleGeometry3::V_0] = vertices[_v0];
    faceVertices[TriangleGeometry3::V_1] = vertices[_v1];
    faceVertices[TriangleGeometry3::V_2] = vertices[_v2];

	// generate id for the new face based on indices of its vertices
	Face::id_t faceId = generateTriID(_v0, _v1, _v2);

    // store the face into the face container
    mBoundaryFaceContainer.insert(faceId, new Face(faceVertices));

	// return the id of the newly created face
	return faceId;
}
//-----------------------------------------------------------------------------
dylab::VertexContainer::Iterator 
dylab::TetraComplex_ModelStructure::vertexIterator()
{
    // return the container iterator
    return BoundaryVertexContainer::Iterator(mBoundaryVertexContainer);
}
//-----------------------------------------------------------------------------
dylab::VertexContainer::ConstIterator 
dylab::TetraComplex_ModelStructure::vertexIterator() const
{
    // return the container iterator
    return BoundaryVertexContainer::ConstIterator(mBoundaryVertexContainer);
}
//-----------------------------------------------------------------------------
dylab::VertexContainer::Reader 
dylab::TetraComplex_ModelStructure::vertexReader() const
{
    // return the container reader
    return BoundaryVertexContainer::Reader(mBoundaryVertexContainer);
}
//-----------------------------------------------------------------------------
dylab::FaceContainer::Iterator 
dylab::TetraComplex_ModelStructure::faceIterator()
{
    // return the container iterator
    return BoundaryFaceContainer::Iterator(mBoundaryFaceContainer);
}
//-----------------------------------------------------------------------------
dylab::FaceContainer::ConstIterator 
dylab::TetraComplex_ModelStructure::faceIterator() const
{
    // return the container iterator
    return BoundaryFaceContainer::ConstIterator(mBoundaryFaceContainer);
}
//-----------------------------------------------------------------------------
dylab::FaceContainer::Reader 
dylab::TetraComplex_ModelStructure::faceReader() const
{
    // return the container reader
    return BoundaryFaceContainer::Reader(mBoundaryFaceContainer);
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::prepareForRendering()
{
    // if topology of the tetra-volume has changed
    if (mBoundaryMeshTopologyNeedsUpdate)
    {
        //TODO
    }

    // if geometry of the tetra-volume has changed
    if (mBoundaryMeshGeometryNeedsUpdate)    
    {
        // walk through all vertices, zero their normals and update its positions
        for (BoundaryVertexContainer::Iterator vertex(mBoundaryVertexContainer); vertex.isValid(); vertex++)
        {
            // get the custom vertex pointer
            static_cast<_Vertex*>((Vertex*)vertex)->updatePosition();        
            vertex->beginNormalCalculation();
        }

        // walk through all faces
        for (BoundaryFaceContainer::Iterator face(mBoundaryFaceContainer); face.isValid(); face++)
        {
            // re-compute face normal
            face->updateNormal();

            // and add it to the normals of all its vertices
            face->distributeNormal();
        }

        // walk through all vertices and re-compute their normals    
        for (BoundaryVertexContainer::Iterator vertex(mBoundaryVertexContainer); vertex.isValid(); vertex++)    
            vertex->endNormalCalculation();    
    }
}
//-----------------------------------------------------------------------------

//===========================================================================================
// ComModelStructure methods
//===========================================================================================
void dylab::TetraComplex_ModelStructure::translate(const Vector3 & _trans)
{
	// translate the points
    for (NodeDataContainer::Iterator nodeData(mNodeDataContainer); nodeData.isValid(); nodeData++)
        nodeData->currState().position += _trans;  
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::rotate(const Vector3 & _axis, real_t _radians)
{
	// build rotation matrix
	Matrix3x3 rotation;
	rotation.buildFromAxisAndAngle(_axis, _radians);

	// rotate the points
    for (NodeDataContainer::Iterator nodeData(mNodeDataContainer); nodeData.isValid(); nodeData++)
        nodeData->currState().position = rotation * nodeData->currState().position;  
}
//-----------------------------------------------------------------------------
void dylab::TetraComplex_ModelStructure::scale(const Vector3 & _scale)
{
	// scale the points
    for (NodeDataContainer::Iterator nodeData(mNodeDataContainer); nodeData.isValid(); nodeData++)
        nodeData->currState().position.mul3(_scale);  
}
//-----------------------------------------------------------------------------

